/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IAdapterManager; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker; import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler; import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.internal.util.Util; /** * This class is a default implementation of <code>IObjectContributorManager</code>. * It provides fast merging of contributions with the following semantics: * <ul> * <li> All of the matching contributors will be invoked per property lookup * <li> The search order from a class with the definition<br> * <code>class X extends Y implements A, B</code><br> * is as follows: * <il> * <li>the target's class: X * <li>X's superclasses in order to <code>Object</code> * <li>a depth-first traversal of the target class's interaces in the order * returned by <code>getInterfaces()</code> (in the example, A and * its superinterfaces then B and its superinterfaces) * </il> * </ul> * * @see IObjectContributor */ public abstract class ObjectContributorManager implements IExtensionChangeHandler { /** * @since 3.1 */ private class ContributorRecord { /** * @param contributor * @param targetType */ public ContributorRecord(IObjectContributor contributor, String targetType) { this.contributor = contributor; this.objectClassName = targetType; } String objectClassName; IObjectContributor contributor; } /** Table of contributors. */ protected Map contributors; /** Cache of object class contributor search paths; <code>null</code> if none. */ protected Map objectLookup; /** Cache of resource adapter class contributor search paths; <code>null</code> if none. */ protected Map resourceAdapterLookup; /** Cache of adaptable class contributor search paths; <code>null</code> if none. */ protected Map adaptableLookup; protected Set contributorRecordSet; /** * Constructs a new contributor manager. */ public ObjectContributorManager() { contributors = new Hashtable(5); contributorRecordSet = new HashSet(5); objectLookup = null; resourceAdapterLookup = null; adaptableLookup = null; String extensionPointId = getExtensionPointFilter(); if (extensionPointId != null) { // RAP [bm] namespace IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint( PlatformUI.PLUGIN_EXTENSION_NAME_SPACE, extensionPointId); IExtensionTracker tracker = PlatformUI.getWorkbench() .getExtensionTracker(); tracker.registerHandler(this, ExtensionTracker .createExtensionPointFilter(extensionPoint)); } } /** * Return the extension point id (local to org.eclipse.ui) that this manager * is associated with. Default implementation returns null, which implies no * relationship with a particular extension. * * @return the extension point id * @since 3.4 */ protected String getExtensionPointFilter() { return null; } /** * Adds contributors for the given types to the result list. */ private void addContributorsFor(List types, List result) { for (Iterator classes = types.iterator(); classes.hasNext();) { Class clazz = (Class) classes.next(); List contributorList = (List) contributors.get(clazz.getName()); if (contributorList != null) { result.addAll(contributorList); } } } /** * Returns the class search order starting with <code>extensibleClass</code>. * The search order is defined in this class' comment. */ protected final List computeClassOrder(Class extensibleClass) { ArrayList result = new ArrayList(4); Class clazz = extensibleClass; while (clazz != null) { result.add(clazz); clazz = clazz.getSuperclass(); } return result; } /** * Returns the interface search order for the class hierarchy described * by <code>classList</code>. * The search order is defined in this class' comment. */ protected final List computeInterfaceOrder(List classList) { ArrayList result = new ArrayList(4); Map seen = new HashMap(4); for (Iterator list = classList.iterator(); list.hasNext();) { Class[] interfaces = ((Class) list.next()).getInterfaces(); internalComputeInterfaceOrder(interfaces, result, seen); } return result; } /** * Flushes the cache of contributor search paths. This is generally required * whenever a contributor is added or removed. * <p> * It is likely easier to just toss the whole cache rather than trying to be * smart and remove only those entries affected. */ public void flushLookup() { objectLookup = null; resourceAdapterLookup = null; adaptableLookup = null; } /** * Cache the real adapter class contributor search path. */ private void cacheResourceAdapterLookup(Class adapterClass, List results) { if (resourceAdapterLookup == null) { resourceAdapterLookup = new HashMap(); } resourceAdapterLookup.put(adapterClass, results); } /** * Cache the real adapter class contributor search path. */ private void cacheAdaptableLookup(String adapterClass, List results) { if (adaptableLookup == null) { adaptableLookup = new HashMap(); } adaptableLookup.put(adapterClass, results); } /** * Cache the object class contributor search path. */ private void cacheObjectLookup(Class objectClass, List results) { if (objectLookup == null) { objectLookup = new HashMap(); } objectLookup.put(objectClass, results); } /** * Get the contributions registered to this manager. * * @return an unmodifiable <code>Collection</code> containing all registered * contributions. The objects in this <code>Collection</code> will be * <code>List</code>s containing the actual contributions. */ public Collection getContributors() { return Collections.unmodifiableCollection(contributors.values()); } /** * Return the list of contributors for the supplied class. */ protected List addContributorsFor(Class objectClass) { List classList = computeClassOrder(objectClass); List result = new ArrayList(); addContributorsFor(classList, result); classList = computeInterfaceOrder(classList); // interfaces addContributorsFor(classList, result); return result; } /** * Returns true if contributors exist in the manager for * this object and any of it's super classes, interfaces, or * adapters. * * @param object the object to test * @return whether the object has contributors */ public boolean hasContributorsFor(Object object) { List contributors = getContributors(object); return contributors.size() > 0; } /** * Add interface Class objects to the result list based * on the class hierarchy. Interfaces will be searched * based on their position in the result list. */ private void internalComputeInterfaceOrder(Class[] interfaces, List result, Map seen) { List newInterfaces = new ArrayList(seen.size()); for (int i = 0; i < interfaces.length; i++) { Class interfac = interfaces[i]; if (seen.get(interfac) == null) { result.add(interfac); seen.put(interfac, interfac); newInterfaces.add(interfac); } } for (Iterator newList = newInterfaces.iterator(); newList.hasNext();) { internalComputeInterfaceOrder(((Class) newList.next()) .getInterfaces(), result, seen); } } /** * Return whether the given contributor is applicable to all elements in the * selection. * * @param selection * the selection * @param contributor * the contributor * @return whether it is applicable */ public boolean isApplicableTo(IStructuredSelection selection, IObjectContributor contributor) { Iterator elements = selection.iterator(); while (elements.hasNext()) { if (contributor.isApplicableTo(elements.next()) == false) { return false; } } return true; } /** * Return whether the given contributor is applicable to all elements in the * list. * * @param list * the selection * @param contributor * the contributor * @return whether it is applicable */ public boolean isApplicableTo(List list, IObjectContributor contributor) { Iterator elements = list.iterator(); while (elements.hasNext()) { if (contributor.isApplicableTo(elements.next()) == false) { return false; } } return true; } /** * Register a contributor. * * @param contributor the contributor * @param targetType the target type */ public void registerContributor(IObjectContributor contributor, String targetType) { List contributorList = (List) contributors.get(targetType); if (contributorList == null) { contributorList = new ArrayList(5); contributors.put(targetType, contributorList); } contributorList.add(contributor); flushLookup(); IConfigurationElement element = (IConfigurationElement) Util.getAdapter(contributor, IConfigurationElement.class); //hook the object listener if (element != null) { ContributorRecord contributorRecord = new ContributorRecord( contributor, targetType); contributorRecordSet.add(contributorRecord); PlatformUI.getWorkbench().getExtensionTracker().registerObject( element.getDeclaringExtension(), contributorRecord, IExtensionTracker.REF_WEAK); } } /** * Unregister all contributors. */ public void unregisterAllContributors() { contributors = new Hashtable(5); flushLookup(); } /** * Unregister a contributor from the target type. * * @param contributor the contributor * @param targetType the target type */ public void unregisterContributor(IObjectContributor contributor, String targetType) { List contributorList = (List) contributors.get(targetType); if (contributorList == null) { return; } contributorList.remove(contributor); if (contributorList.isEmpty()) { contributors.remove(targetType); } flushLookup(); } /** * Unregister all contributors for the target type. * * @param targetType the target type */ public void unregisterContributors(String targetType) { contributors.remove(targetType); flushLookup(); } protected List getContributors(Object object) { // Determine is the object is a resource Object resource = LegacyResourceSupport.getAdaptedContributorResource(object); // Fetch the unique adapters List adapters = new ArrayList(Arrays.asList(Platform.getAdapterManager().computeAdapterTypes(object.getClass()))); removeCommonAdapters(adapters, Arrays.asList(new Class[] {object.getClass()})); List contributors = new ArrayList(); // Calculate the contributors for this object class addAll(contributors, getObjectContributors(object.getClass())); // Calculate the contributors for resource classes if(resource != null) { addAll(contributors, getResourceContributors(resource.getClass())); } // Calculate the contributors for each adapter type if(adapters != null) { for (Iterator it = adapters.iterator(); it.hasNext();) { String adapter = (String) it.next(); addAll(contributors, getAdaptableContributors(adapter)); } } // Remove duplicates. Note: this -must- maintain the element order to preserve menu order. contributors = removeDups(contributors); return contributors.isEmpty() ? Collections.EMPTY_LIST : new ArrayList(contributors); } /** * Returns the contributions for the given class. This considers * contributors on any super classes and interfaces. * * @param objectClass the class to search for contributions. * @return the contributions for the given class. This considers * contributors on any super classes and interfaces. * */ protected List getObjectContributors(Class objectClass) { List objectList = null; // Lookup the results in the cache first. if (objectLookup != null) { objectList = (List) objectLookup.get(objectClass); } if (objectList == null) { objectList = addContributorsFor(objectClass); if (objectList.size() == 0) { objectList = Collections.EMPTY_LIST; } else { objectList = Collections.unmodifiableList(objectList); } cacheObjectLookup(objectClass, objectList); } return objectList; } /** * Returns the contributions for the given <code>IResource</code>class. * This considers contributors on any super classes and interfaces. This * will only return contributions that are adaptable. * * @param resourceClass the class to search for contributions. * @return the contributions for the given class. This considers * adaptable contributors on any super classes and interfaces. * */ protected List getResourceContributors(Class resourceClass) { List resourceList = null; if (resourceAdapterLookup != null) { resourceList = (List) resourceAdapterLookup.get(resourceClass); } if (resourceList == null) { resourceList = addContributorsFor(resourceClass); if (resourceList.size() == 0) { resourceList = Collections.EMPTY_LIST; } else { resourceList = Collections.unmodifiableList(filterOnlyAdaptableContributors(resourceList)); } cacheResourceAdapterLookup(resourceClass, resourceList); } return resourceList; } /** * Returns the contributions for the given type name. * * @param adapterType the class to search for contributions. * @return the contributions for the given class. This considers * contributors to this specific type. * */ protected List getAdaptableContributors(String adapterType) { List adaptableList = null; // Lookup the results in the cache first, there are two caches // one that stores non-adapter contributions and the other // contains adapter contributions. if (adaptableLookup != null) { adaptableList = (List) adaptableLookup.get(adapterType); } if (adaptableList == null) { // ignore resource adapters because these must be adapted via the // IContributorResourceAdapter. if (LegacyResourceSupport.isResourceType(adapterType) || LegacyResourceSupport.isResourceMappingType(adapterType)) { adaptableList = Collections.EMPTY_LIST; } else { adaptableList = (List) contributors.get(adapterType); if (adaptableList == null || adaptableList.size() == 0) { adaptableList = Collections.EMPTY_LIST; } else { adaptableList = Collections.unmodifiableList(filterOnlyAdaptableContributors(adaptableList)); } } cacheAdaptableLookup(adapterType, adaptableList); } return adaptableList; } /** * Prunes from the list of adapters type names that are in the class * search order of every class in <code>results</code>. * @param adapters * @param results * @since 3.1 */ protected void removeCommonAdapters(List adapters, List results) { for (Iterator it = results.iterator(); it.hasNext();) { Class clazz = ((Class) it.next()); List commonTypes = computeCombinedOrder(clazz); for (Iterator it2 = commonTypes.iterator(); it2.hasNext();) { Class type = (Class) it2.next(); adapters.remove(type.getName()); } } } /** * Returns the class search order starting with <code>extensibleClass</code>. * The search order is defined in this class' comment. */ protected List computeCombinedOrder(Class inputClass) { List result = new ArrayList(4); Class clazz = inputClass; while (clazz != null) { // add the class result.add(clazz); // add all the interfaces it implements Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { result.add(interfaces[i]); } // get the superclass clazz = clazz.getSuperclass(); } return result; } private List filterOnlyAdaptableContributors(List contributors) { List adaptableContributors = null; for (Iterator it = contributors.iterator(); it.hasNext();) { IObjectContributor c = (IObjectContributor) it.next(); if(c.canAdapt()) { if(adaptableContributors == null) { adaptableContributors = new ArrayList(); } adaptableContributors.add(c); } } return adaptableContributors == null ? Collections.EMPTY_LIST : adaptableContributors; } /* (non-Javadoc) * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension, java.lang.Object[]) */ public void removeExtension(IExtension source, Object[] objects) { for (int i = 0; i < objects.length; i++) { if (objects[i] instanceof ContributorRecord) { ContributorRecord contributorRecord = (ContributorRecord) objects[i]; unregisterContributor((contributorRecord).contributor, (contributorRecord).objectClassName); contributorRecordSet.remove(contributorRecord); } } } /** * Remove listeners and dispose of this manager. * */ public void dispose() { if(getExtensionPointFilter() != null) { PlatformUI.getWorkbench().getExtensionTracker().unregisterHandler(this); } } /** * Returns the list of contributors that are interested in the * given list of model elements. * @param elements a list of model elements (<code>Object</code>) * @return the list of interested contributors (<code>IObjectContributor</code>) */ protected List getContributors(List elements) { // Calculate the common class, interfaces, and adapters registered // via the IAdapterManager. List commonAdapters = new ArrayList(); List commonClasses = getCommonClasses(elements, commonAdapters); // Get the resource class. It will be null if any of the // elements are resources themselves or do not adapt to // IResource. Class resourceClass = getCommonResourceClass(elements); Class resourceMappingClass = getResourceMappingClass(elements); // Get the contributors. List contributors = new ArrayList(); // Add the resource contributions to avoid duplication if (resourceClass != null) { addAll(contributors, getResourceContributors(resourceClass)); } if (commonClasses != null && !commonClasses.isEmpty()) { for (int i = 0; i < commonClasses.size(); i++) { List results = getObjectContributors((Class) commonClasses .get(i)); addAll(contributors, results); } } // Add the resource mappings explicitly to avoid possible duplication if (resourceMappingClass == null) { // Still show the menus if the object is not adaptable but the adapter manager // has an entry for it resourceMappingClass = LegacyResourceSupport .getResourceMappingClass(); if (resourceMappingClass != null && commonAdapters.contains(resourceMappingClass.getName())) { addAll(contributors, getResourceContributors(resourceMappingClass)); } } else { contributors.addAll(getResourceContributors(resourceMappingClass)); } if (!commonAdapters.isEmpty()) { for (Iterator it = commonAdapters.iterator(); it.hasNext();) { String adapter = (String) it.next(); addAll(contributors, getAdaptableContributors(adapter)); } } // Remove duplicates. Note: this -must- maintain the element order to preserve menu order. contributors = removeDups(contributors); return contributors.isEmpty() ? Collections.EMPTY_LIST : new ArrayList(contributors); } /** * Adds all items in toAdd to the given collection. Optimized to avoid creating an iterator. * This assumes that toAdd is efficient to index (i.e. it's an ArrayList or some other RandomAccessList), * which is the case for all uses in this class. */ private static void addAll(Collection collection, List toAdd) { for (int i = 0, size = toAdd.size(); i < size; ++i) { collection.add(toAdd.get(i)); } } /** * Removes duplicates from the given list, preserving order. */ private static List removeDups(List list) { if (list.size() <= 1) { return list; } HashSet set = new HashSet(list); if (set.size() == list.size()) { return list; } ArrayList result = new ArrayList(set.size()); for (Iterator i = list.iterator(); i.hasNext();) { Object o = i.next(); if (set.remove(o)) { result.add(o); } } return result; } /** * Returns the common denominator class, interfaces, and adapters * for the given collection of objects. */ private List getCommonClasses(List objects, List commonAdapters) { if (objects == null || objects.size() == 0) { return null; } // Optimization: if n==1 (or if all objects are of the same class), then the common class is the object's class, // and the common adapters are the adapters cached for that class in the adapter manager // See bug 177592 for more details. if (allSameClass(objects)) { Class clazz = objects.get(0).getClass(); commonAdapters.addAll(Arrays.asList(Platform.getAdapterManager().computeAdapterTypes(clazz))); List result = new ArrayList(1); result.add(clazz); return result; } // Compute all the super classes, interfaces, and adapters // for the first element. List classes = computeClassOrder(objects.get(0).getClass()); List adapters = computeAdapterOrder(classes); List interfaces = computeInterfaceOrder(classes); // Cache of all types found in the selection - this is needed // to compute common adapters. List lastCommonTypes = new ArrayList(); boolean classesEmpty = classes.isEmpty(); boolean interfacesEmpty = interfaces.isEmpty(); // Traverse the selection if there is more than one element selected. for (int i = 1; i < objects.size(); i++) { // Compute all the super classes for the current element List otherClasses = computeClassOrder(objects.get(i).getClass()); if (!classesEmpty) { classesEmpty = extractCommonClasses(classes, otherClasses); } // Compute all the interfaces for the current element // and all of its super classes. List otherInterfaces = computeInterfaceOrder(otherClasses); if (!interfacesEmpty) { interfacesEmpty = extractCommonClasses(interfaces, otherInterfaces); } // Compute all the adapters provided for the calculated // classes and interfaces for this element. List classesAndInterfaces = new ArrayList(otherClasses); if (otherInterfaces != null) { classesAndInterfaces.addAll(otherInterfaces); } List otherAdapters = computeAdapterOrder(classesAndInterfaces); // Compute common adapters // Note here that an adapter can match a class or interface, that is // that an element in the selection may not adapt to a type but instead // be of that type. // If the selected classes doesn't have adapters, keep // adapters that match the given classes types (classes and interfaces). if (otherAdapters.isEmpty() && !adapters.isEmpty()) { removeNonCommonAdapters(adapters, classesAndInterfaces); } else { if (adapters.isEmpty()) { removeNonCommonAdapters(otherAdapters, lastCommonTypes); if (!otherAdapters.isEmpty()) { adapters.addAll(otherAdapters); } } else { // Remove any adapters of the first element that // are not in the current element's adapter list. for (Iterator it = adapters.iterator(); it.hasNext();) { String adapter = (String) it.next(); if (!otherAdapters.contains(adapter)) { it.remove(); } } } } // Remember the common search order up to now, this is // used to match adapters against common classes or interfaces. lastCommonTypes.clear(); lastCommonTypes.addAll(classes); lastCommonTypes.addAll(interfaces); if (interfacesEmpty && classesEmpty && adapters.isEmpty()) { // As soon as we detect nothing in common, just exit. return null; } } // Once the common classes, interfaces, and adapters are // calculated, let's prune the lists to remove duplicates. ArrayList results = new ArrayList(4); ArrayList superClasses = new ArrayList(4); if (!classesEmpty) { for (int j = 0; j < classes.size(); j++) { if (classes.get(j) != null) { superClasses.add(classes.get(j)); } } // Just keep the first super class if (!superClasses.isEmpty()) { results.add(superClasses.get(0)); } } if (!interfacesEmpty) { removeCommonInterfaces(superClasses, interfaces, results); } // Remove adapters already included as common classes if (!adapters.isEmpty()) { removeCommonAdapters(adapters, results); commonAdapters.addAll(adapters); } return results; } /** * Returns <code>true</code> if all objects in the given list are of the same class, * <code>false</code> otherwise. */ private boolean allSameClass(List objects) { int size = objects.size(); if (size <= 1) return true; Class clazz = objects.get(0).getClass(); for (int i = 1; i < size; ++i) { if (!objects.get(i).getClass().equals(clazz)) { return false; } } return true; } private boolean extractCommonClasses(List classes, List otherClasses) { boolean classesEmpty = true; if (otherClasses.isEmpty()) { // When no super classes, then it is obvious there // are no common super classes with the first element // so clear its list. classes.clear(); } else { // Remove any super classes of the first element that // are not in the current element's super classes list. for (int j = 0; j < classes.size(); j++) { if (classes.get(j) != null) { classesEmpty = false; // TODO: should this only be set if item not nulled out? if (!otherClasses.contains(classes.get(j))) { classes.set(j, null); } } } } return classesEmpty; } private void removeNonCommonAdapters(List adapters, List classes) { for (int i = 0; i < classes.size(); i++) { Object o = classes.get(i); if (o != null) { Class clazz = (Class) o; String name = clazz.getName(); if (adapters.contains(name)) { return; } } } adapters.clear(); } private void removeCommonInterfaces(List superClasses, List types, List results) { List dropInterfaces = null; if (!superClasses.isEmpty()) { dropInterfaces = computeInterfaceOrder(superClasses); } for (int j = 0; j < types.size(); j++) { if (types.get(j) != null) { if (dropInterfaces != null && !dropInterfaces.contains(types.get(j))) { results.add(types.get(j)); } } } } private List computeAdapterOrder(List classList) { Set result = new HashSet(4); IAdapterManager adapterMgr = Platform.getAdapterManager(); for (Iterator list = classList.iterator(); list.hasNext();) { Class clazz = ((Class) list.next()); String[] adapters = adapterMgr.computeAdapterTypes(clazz); for (int i = 0; i < adapters.length; i++) { String adapter = adapters[i]; if (!result.contains(adapter)) { result.add(adapter); } } } return new ArrayList(result); } /** * Returns the common denominator resource class for the given * collection of objects. * Do not return a resource class if the objects are resources * themselves so as to prevent double registration of actions. */ private Class getCommonResourceClass(List objects) { if (objects == null || objects.size() == 0) { return null; } Class resourceClass = LegacyResourceSupport.getResourceClass(); if (resourceClass == null) { // resources plug-in not loaded - no resources. period. return null; } List testList = new ArrayList(objects.size()); for (int i = 0; i < objects.size(); i++) { Object object = objects.get(i); if (object instanceof IAdaptable) { if (resourceClass.isInstance(object)) { continue; } Object resource = LegacyResourceSupport .getAdaptedContributorResource(object); if (resource == null) { //Not a resource and does not adapt. No common resource class return null; } testList.add(resource); } else { return null; } } return getCommonClass(testList); } /** * Return the ResourceMapping class if the elements all adapt to it. */ private Class getResourceMappingClass(List objects) { if (objects == null || objects.size() == 0) { return null; } Class resourceMappingClass = LegacyResourceSupport .getResourceMappingClass(); if (resourceMappingClass == null) { // resources plug-in not loaded - no resources. period. return null; } for (int i = 0; i < objects.size(); i++) { Object object = objects.get(i); if (object instanceof IAdaptable) { if (resourceMappingClass.isInstance(object)) { continue; } Object resourceMapping = LegacyResourceSupport .getAdaptedContributorResourceMapping(object); if (resourceMapping == null) { //Not a resource and does not adapt. No common resource class return null; } } else { return null; } } // If we get here then all objects adapt to ResourceMapping return resourceMappingClass; } /** * Returns the common denominator class for the given * collection of objects. */ private Class getCommonClass(List objects) { if (objects == null || objects.size() == 0) { return null; } Class commonClass = objects.get(0).getClass(); // try easy if (objects.size() == 1) { return commonClass; // try harder } for (int i = 1; i < objects.size(); i++) { Object object = objects.get(i); Class newClass = object.getClass(); // try the short cut if (newClass.equals(commonClass)) { continue; } // compute common class commonClass = getCommonClass(commonClass, newClass); // give up if (commonClass == null) { return null; } } return commonClass; } /** * Returns the common denominator class for * two input classes. */ private Class getCommonClass(Class class1, Class class2) { List list1 = computeCombinedOrder(class1); List list2 = computeCombinedOrder(class2); for (int i = 0; i < list1.size(); i++) { for (int j = 0; j < list2.size(); j++) { Class candidate1 = (Class) list1.get(i); Class candidate2 = (Class) list2.get(j); if (candidate1.equals(candidate2)) { return candidate1; } } } // no common class return null; } }